package hku.cs.lunuo.rank.model.impl;

import hku.cs.lunuo.rank.StatisticProperty;
import hku.cs.lunuo.rank.model.Label;
import hku.cs.lunuo.rank.model.Statistic;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;

public class BlkStatistic extends Statistic{
	HashMap<String, HashMap<String, HashMap<Integer, StatisticProperty>>> data = 
		new HashMap<String, HashMap<String, HashMap<Integer, StatisticProperty>>>();
	public void add(String className, String methodName, int idx, int times,
			boolean tof) {
		HashMap<String, HashMap<Integer, StatisticProperty>> HBS;
		HBS = data.get(className);
		if(HBS == null) {
			HBS = new HashMap<String, HashMap<Integer, StatisticProperty>>();
		}
		HashMap<Integer, StatisticProperty> inHBS;
		inHBS = HBS.get(methodName);
		if(inHBS==null) {
			inHBS = new HashMap<Integer, StatisticProperty>();
		}
		StatisticProperty cbs = inHBS.get(idx);
		if(cbs == null) {
			cbs = new StatisticProperty();
		}
		
		cbs.add(tof);
		inHBS.put(idx, cbs);
		HBS.put(methodName, inHBS);
		data.put(className, HBS);
	}
	public Set<String> getKlassNames() {
		return data.keySet();
	}
	public Set<String> getMethodNames(String klassName) {
		return data.get(klassName).keySet();
	}
	public Set<Integer> getBlks(String klassName, String methodName) {
		return data.get(klassName).get(methodName).keySet();
	}
	public StatisticProperty getProperty(String klassName, String methodName, Integer idx) {
		return data.get(klassName).get(methodName).get(idx);
	}
	public void setLabel(String klassName, String methodName, Integer idx, Label label) {
		data.get(klassName).get(methodName).get(idx).label = label;
		
	}
	
	public HashMap<Integer, StatisticProperty> getBlksInMeth(String klassName, String methodName){
		return data.get(klassName).get(methodName);
	}
	public void setStatisticProperty(String klassName, String methodName, HashMap<Integer, StatisticProperty> blkData) {
		HashMap<String, HashMap<Integer, StatisticProperty>> tmp = data.get(klassName);
		if(tmp == null)
			tmp = new HashMap<String, HashMap<Integer, StatisticProperty>>();
		tmp.put(methodName, blkData);
		data.put(klassName, tmp);	
	}
	
	public boolean isIn(String className, String methodName, int idx) {
		if(data.get(className)==null)
			return false;
		else if(data.get(className).get(methodName)==null)
			return false;
		else {
			return data.get(className).get(methodName).containsKey(idx);
		}
	}
	
	public void merge(BlkStatistic bs){
		this.totaltf.add(bs.totaltf);
		if(this.data.size()==0){
			this.data = bs.data;
			return;
		}	
		
		HashMap<String, HashMap<String, HashMap<Integer, StatisticProperty>>> bsData = bs.data;
		for(String klassName : bsData.keySet()){
			HashMap<String, HashMap<Integer, StatisticProperty>> yHBS 
				= this.data.get(klassName);
			HashMap<String, HashMap<Integer, StatisticProperty>> HBS = bsData.get(klassName);
			if (yHBS!=null){
				for(String methodName : HBS.keySet()){
					HashMap<Integer, StatisticProperty> yInHBS = yHBS.get(methodName);
					HashMap<Integer, StatisticProperty> inHBS = HBS.get(methodName);
					if(yInHBS != null){
						for(Integer blkIdx : inHBS.keySet()){
							StatisticProperty ysp = yInHBS.get(blkIdx);
							StatisticProperty sp = inHBS.get(blkIdx);
							if(ysp != null)
								ysp.merge(sp);
							else
								yInHBS.put(blkIdx, sp);
						}
					}else{
						yInHBS = inHBS;
						yHBS.put(methodName, yInHBS);
					}
				}
			}else{
				yHBS = HBS;
				this.data.put(klassName, yHBS);
			}
		}
	
	}
	
	public void toXML(File f) {
		Element xmlRoot = new Element("BlkStatistics");
		Document xmlDoc = new Document(xmlRoot);
		int i=0;
		for(String klassName : data.keySet()) {
			for(String methodName: data.get(klassName).keySet()) {
				for(Integer idx : data.get(klassName).get(methodName).keySet()) {
					Element xes = new Element("block");
					xes.setAttribute("class", klassName);
					xes.setAttribute("method", methodName);
					xes.setAttribute("idx", String.valueOf(idx));
					data.get(klassName).get(methodName).get(idx).toXML(xes);
					xmlRoot.addContent(xes);
					i++;
					xes.setAttribute("index", String.valueOf(i));
				}
			}
		}
		xmlRoot.setAttribute("number", String.valueOf(i));
		
		try {
			new XMLOutputter().output(xmlDoc, new FileOutputStream(f));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void toSortedXML(File f) {
		List<BlkStatisticUnit> tmpData = new ArrayList<BlkStatisticUnit>();
		for(String klassName : data.keySet()) {
			for(String methodName: data.get(klassName).keySet()) {
				for(Integer idx : data.get(klassName).get(methodName).keySet()) {
					tmpData.add(new BlkStatisticUnit(klassName, methodName, idx, data.get(klassName).get(methodName).get(idx)));
				}
			}
		}
		Collections.sort(tmpData);
		Collections.reverse(tmpData);
		
		Element xmlRoot = new Element("EsStatistics");
		Document xmlDoc = new Document(xmlRoot);
		xmlRoot.setAttribute("number", String.valueOf(tmpData.size()));
		int i = 0;
		for(BlkStatisticUnit esu : tmpData) {
			Element xml = new Element("block");
			xml.setAttribute("index", Integer.toString(++i));
			esu.toXML(xml);
			xmlRoot.addContent(xml);
		}
		
		try {
			new XMLOutputter().output(xmlDoc, new FileOutputStream(f));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
